package srg

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/redis/go-redis/v9"
)

var defMsgQueue string
var delayMsgList string
var queueMsgHead string

const oriMsgHead = "ORI.MSG"
const deadMsgHead = "DEAD.MSG"

type SrgMsg struct {
	Id          int64  `json:"id"`
	QueueName   string `json:"queueName"`
	MsgType     string `json:"msgType"`
	MsgTargetId string `json:"msgTargetId"`
	MsgInfo     string `json:"msgInfo"`
	MsgVer      int64  `json:"msgVer"` //暂未启用
	TryCount    int8   `json:"tryCount"`
	TiggerTime  int64  `json:"tiggerTime"` //触发时间
}

type SrgDeadMsg struct {
	SrgMsg
	ErrorInfo string `json:"errorInfo"`
}

type SrgMsgWorker struct {
	DoWork       func(msgId int64, msgType, msgTargetId, msgInfo string) error
	MatchMsgType func(msgType string) bool
	MatchMsgVer  func()
}

var srgMsgWorkers []SrgMsgWorker

var delayLock [6]string = [6]string{}
var delayLockIndex int = 0

// 具体消息处理
// msgPayload 消息id
func dealMsg(msgPayload string) {

	lockKey := fmt.Sprintf("%s%s:%s", srgHead, "DEALMSG", msgPayload)

	needDelay := false
	for i := 0; i < 6; i++ {
		if delayLock[i] == msgPayload {
			needDelay = true
			break
		}
	}
	if needDelay {
		time.Sleep(time.Millisecond * 100)
	}

	//抢处理锁
	loc := DLockAdd(lockKey, time.Second*9)

	if !loc {
		return
	}

	//获取消息详情
	msgBytes, err := srds.Get(ctx, fmt.Sprintf("%s%s:%s", srgHead, oriMsgHead, msgPayload)).Result()
	if err != nil {
		return
	}

	var msgInfo SrgMsg
	json.Unmarshal([]byte(msgBytes), &msgInfo)

	if msgInfo.TiggerTime > time.Now().UnixMilli() { //延迟消息 不处理
		return
	}

	var matchWork *SrgMsgWorker = nil

	for _, v := range srgMsgWorkers { //匹配消息处理者
		if v.MatchMsgType(msgInfo.MsgType) {
			matchWork = &v
			continue
		}
	}

	if matchWork == nil { //没有处理者，等待下一个兜底触发
		//下一个兜底，让出处理锁100ms
		delayLock[delayLockIndex] = msgPayload
		delayLockIndex = (delayLockIndex + 1) % 6
		srds.Publish(ctx, defMsgQueue, msgInfo.Id)
		return
	}

	errr := matchWork.DoWork(msgInfo.Id, msgInfo.MsgType, msgInfo.MsgTargetId, msgInfo.MsgInfo)

	if errr != nil { //处理有错
		//处理失败 标记失败
		msgInfo.TryCount++

		if msgInfo.TryCount > 3 {
			//失败次数超过3次  放入死信队列
			deadMsg := SrgDeadMsg{
				SrgMsg: msgInfo,
			}

			msgBytes, _ := json.Marshal(deadMsg)
			err := srds.Set(ctx, fmt.Sprintf("%s%s:%d", srgHead, deadMsgHead, deadMsg.Id), string(msgBytes), 0).Err()
			if err == nil {
				srds.Del(ctx, fmt.Sprintf("%s%s:%s", srgHead, oriMsgHead, msgPayload))
			}

		} else {
			//失败次数未超过3次  重新放入队列
			msgBytes, _ := json.Marshal(msgInfo)
			srds.Set(ctx, fmt.Sprintf("%s%s:%s", srgHead, oriMsgHead, msgPayload), string(msgBytes), 0)
		}

	} else {
		//处理完成删除消息
		srds.Del(ctx, fmt.Sprintf("%s%s:%s", srgHead, oriMsgHead, msgPayload))
	}

	if msgInfo.QueueName != "" { //队列消息处理
		DLockRelease("QUEUE_LOCK:" + msgInfo.QueueName)
		popQueueMsgByQueueName(queueMsgHead + msgInfo.QueueName)
	}

}

// 兜底处理 处理未被处理的消息
func dealExMsg() {

	keys, _ := srds.Keys(ctx, fmt.Sprintf("%s%s:*", srgHead, oriMsgHead)).Result()

	for _, v := range keys {

		s := strings.Split(v, ":")

		id := s[2]
		id = id[0 : len(id)-6]
		ttime, _ := strconv.ParseInt(id, 10, 64)
		//判断是否过期
		if time.Now().Unix() <= ttime {
			continue
		}

		lockKey := fmt.Sprintf("%s%s:%s", srgHead, "DEALMSG", s[2])

		lockExist, err := DLockExist(lockKey)
		if err != nil {
			continue
		}
		if !lockExist {
			fmt.Println("兜底" + s[2])
			srds.Publish(ctx, defMsgQueue, s[2]).Err()
		}

	}

}

// 处理延迟消息
func dealDelayMsg() {

	//获取延迟消息
	keys, _ := srds.ZRangeByScoreWithScores(ctx, delayMsgList, &redis.ZRangeBy{
		Min: "-inf",
		Max: fmt.Sprintf("%d", time.Now().UnixMicro()+15000000), //预加载15s
	}).Result()

	for _, v := range keys {

		//转换为即时消息
		var msgInfo SrgMsg
		json.Unmarshal([]byte(v.Member.(string)), &msgInfo)

		srds.Set(ctx, fmt.Sprintf("%s%s:%d", srgHead, oriMsgHead, msgInfo.Id), v.Member.(string), 0)

		go func(sleepTime int64) {
			if sleepTime > 0 {
				time.Sleep(time.Duration(sleepTime) * time.Millisecond)
			}
			srds.Publish(ctx, defMsgQueue, msgInfo.Id)
		}(msgInfo.TiggerTime - time.Now().UnixMilli())

		//删除zset数据
		srds.ZRem(ctx, delayMsgList, v.Member)

	}

}
